#include "emulator.h"
#include "emulator_priv.h"

#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <errno.h>

struct savefile {
	struct state state;
	int16_t memory[MEMORY_SIZE];
};

static size_t save_file_size(enum filetype type) {
	switch(type) {
	case FILETYPE_SMALL:	return sizeof(struct savefile);
	case FILETYPE_COMPLETE:	return sizeof(struct machine);
	case FILETYPE_ASCII:	return 6 + 14*5 + 5*MEMORY_SIZE + 1;
	default:				return 0;
	}
}

void save_ascii_number(char **string, int16_t number, bool linebreak) {

	/* propogate errors if we need to */
	if(!*string) return;

	/* print the value, indicate failure */
	if(5 != snprintf(*string, 6, "%04hx%c", number, linebreak ? '\n' : ' ')) {
		*string = 0;
		return;
	}

	/* adjust the sting pointer */
	*string += 5;
}

size_t save_machine(machine_t mmm, enum filetype type, void *buffer, size_t length) {
	size_t size = save_file_size(type);
	if(!size) return 0; //unknown type
	if(!buffer) return size; //size request
	if(length < size) return 0; //insufficient space
	switch(type) {
	case FILETYPE_SMALL: {
		struct savefile *save = buffer;
		save->state = STATE;
		int32_t i;
		for(i = 0; i < MEMORY_SIZE; i++) 
			save->memory[i] = p_mem_read(mmm, i, false);
		break;
	}
	case FILETYPE_COMPLETE: {
		*(machine_t) buffer = *mmm;
		break;
	}
	case FILETYPE_ASCII: {
		int i;
		char *string = buffer;

		/* print single-bit state */
		if(6 != snprintf(string, 7, "%i %i %i\n", STATE.x, STATE.u, STATE.f)) return 0;
		string += 6;

		/* print rest of state */
		for(i = 0; i < 8; i++) save_ascii_number(&string, STATE._r[i], true);
		for(i = 0; i < 4; i++) save_ascii_number(&string, STATE.t[i], true);
		save_ascii_number(&string, STATE.i, true);
		save_ascii_number(&string, STATE.e, true);

		/* print memory */
		for(i = 0; i < MEMORY_SIZE/8; i++) {
			int j;
			for(j = 0; j < 8; j++) {
				save_ascii_number(&string,
					p_mem_read(mmm, 8*i + j, false),
					j == 7);
			}
		}

		if(!*string) return 0;
		break;
	}}
	return size;
}

long load_ascii_number(char **string) {

	/* propogate errors if we need to */
	if(!*string) return 0;

	/* read in the number */
	errno = 0;
	long number = strtol(*string, string, 16);

	/* on error, null out the string pointer */
	if(errno) {
		*string = 0;
		return 0;
	}

	/* consume a mandatory trailing whitespace */
	++*string;

	/* got the number */
	return number;
}

machine_t load_machine(enum filetype type, void *buffer, size_t length) {
	size_t size = save_file_size(type);
	if(!size) return 0; //unknown type
	if(length != size) return 0; //bad length
	machine_t mmm = create_machine();
	switch(type) {
	case FILETYPE_SMALL: {
		struct savefile *save = buffer;
		STATE = save->state;
		int32_t i;
		for(i = 0; i < MEMORY_SIZE; i++) {
			edit_state(mmm);
			p_mem_write(mmm, i, save->memory[i], false);
		}
		break;
	}
	case FILETYPE_COMPLETE: {
		*mmm = *(machine_t) buffer;
		break;
	}
	case FILETYPE_ASCII: {
		int i;
		char *string = buffer;

		STATE.x = (bool) load_ascii_number(&string);
		STATE.u = (bool) load_ascii_number(&string);
		STATE.f = (bool) load_ascii_number(&string);

		for(i = 0; i < 8; i++) STATE._r[i] = (int16_t) load_ascii_number(&string);
		for(i = 0; i < 4; i++) STATE.t[i] = (int16_t) load_ascii_number(&string);
		STATE.i = load_ascii_number(&string);
		STATE.e = load_ascii_number(&string);

		for(i = 0; i < MEMORY_SIZE; i++) {
			edit_state(mmm);
			p_mem_write(mmm, i, (int16_t) load_ascii_number(&string), false);
		}

		if(!string) {
			destroy_machine(mmm);
			mmm = 0;
		}

		break;
	}}

	/* clear out transients */
	bzero(&mmm->transient, sizeof(mmm->transient));

	return mmm;
}

